#ifndef SOCKET_HPP_
#define SOCKET_HPP_

#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>

namespace SystemSocket
{
	class Connection;

	/**
	 * The purpose of this class is to hide POSIX socket implementation so that
	 * user of this class may use it according to object oriented standars. This
	 * class doesn't provide the full interface of POSIX sockets, but only those
	 * that were needed to finish followmaze task.
	 */
	class Socket
	{
		public:
			/**
			 * The method creates new Socket on the given port with the given queue size.
			 * For more information check POSIX Socket documentation.
			 *
			 * \param iPort value of the port the socket should be open.
			 * \param iBacklog the maximum number of connections waiting on the socket.
			 */
			Socket(unsigned int iPortNumber, unsigned int iBacklog):
				_portNumber(iPortNumber),_backlog(iBacklog),_sockfd(-1){}
			/**
			 * Just destructor. But close the socket if it was open.
			 */
			virtual ~Socket();
			/**
			 * The method opens socket and wraps some of pthreads. Throws exception
			 * SocketCreationException and SocketBind exception if something goes wrong.
			*/
			virtual void openSocket();
			/**
			 * The method wraps the listen method of POSIX. Throws SocketListenCreation.
			 */
			virtual void listen();
			/**
			 * Sets socket to work in non blocking mode. Wraps POSIX fcntl call.
			 */
			void setNonBlocking();
			/**
			 * The method tries to accept incoming connection. If success returns newly
			 * created connection. Otherwise return NULL. It wraps POSIX accept method.
			 *
			 * \return New connection if accept success. Otherwise NULL.
			 */
			virtual Connection * accept();
			/**
			 * It wraps POSIX close call. The method closes the socket if it wasn't closed
			 * yet.
			 */
			virtual void closeSocket();

		private:
			// socket id
			int _sockfd;
			// port number
			unsigned int _portNumber;
			// maximum queue size
			unsigned int _backlog;

			// helpful method
			void * getInAddr(struct sockaddr * aSocketAddr)
			{
				if (aSocketAddr->sa_family == AF_INET)
				{
					return &(((struct sockaddr_in*)aSocketAddr)->sin_addr);
				}
				return &(((struct sockaddr_in6*)aSocketAddr)->sin6_addr);
			}


};
}


#endif /* SOCKET_HPP_ */
